// 聊天模块：业务逻辑
package service

import (
	"encoding/json"
	"im-v1/config"
	"im-v1/database"
	"im-v1/model"
	"log"
	"time"
)

// 单点私发
// 返回: 状态码 和 详情说明
// 0. 首先比较字符串：长度小的在前，长度相同则直接比较，选择小的在前 作为key -> 方法 service.compareAndSwap
// 1. 读取redis看是否存在key，不存在则读取数据库，查看双方是否为好友， 一方不是 -> 返回410
// 2. 存入key，value为二者的secret
// 3. 记录信息到 model.ChatLog
// 4. 将信息存到接收方的redis队列中，存储对象 model.ChatOds
func SendPrivateMsg(mailFrom, mailTo, chatBody string) (int, string) {
	// 获取key
	p, q := compareAndSwap(mailTo, mailFrom)
	key := p + config.ChatConnector + q
	value, err := database.Redis.Get(key).Result()

	// redis不存在
	if err != nil || value == "" {
		f1, u1 := model.NewRelationship(p, q, 1).IsAlreadyGoodFriend()
		f2, u2 := model.NewRelationship(q, p, 1).IsAlreadyGoodFriend()
		if !(f1 && f2) { // 非好友
			return 410, "It's not your best friend anymore"
		}
		// 存入新的key
		database.Redis.Set(key, u1.Secret+u2.Secret, 10*time.Hour)
	}

	// 记录信息
	chat := model.NewChatLog(mailFrom, mailTo, chatBody)
	chat.Insert()

	// 将信息存储到对应接收方mailTo的list
	c, err := json.Marshal(chat.TransformOds())
	if err != nil {
		log.Println("[chat_service SendPrivateMsg] ", err)
		return 400, "bad request"
	}
	database.Redis.RPush(config.ChatMail+mailTo, c)
	return 200, "success"
}

// 接收信息
// 读取自身的redis队列中
func GetMsg(mailbox string) []*model.ChatOds {

	// 从数据库中获取数据
	key := config.ChatMail + mailbox
	chatsLen := database.Redis.LLen(key).Val()
	if chatsLen == 0 { // 无数据
		return nil
	}

	// 防止一次弹出过多，一次最多50条
	if chatsLen > 50 {
		chatsLen = 50
	}
	chats := make([]*model.ChatOds, chatsLen)
	// 遍历添加
	for i := int64(0); i < chatsLen; i++ {
		pop, err := database.Redis.LPop(key).Bytes()
		if err != nil {
			log.Println("[chat_srevice GetMsg] ", err)
			return chats
		}
		chat := &model.ChatOds{}
		err = json.Unmarshal(pop, chat)
		if err != nil {
			log.Println("[chat_srevice GetMsg] ", err)
			return chats
		}
		chats[i] = chat
	}

	return chats
}

// 字符串比较方法
// 长度小的在前，长度相同则直接比较，选择小的在前
func compareAndSwap(a, b string) (string, string) {
	if len(a) > len(b) || (len(a) == len(b) && a > b) {
		return b, a
	}
	return a, b
}
